package eval.host;

import java.util.ArrayList;
import java.util.List;
import eval.vm.VirtualMachine;

/**
 * This class represent a single host instance. 
 * 
 * @author Amit Peled   <amit.kapina@gmail.com>
 * @author Igal Kaufman <igal.kaufman@gmail.com>
 */
public class Host {
	private List<VirtualMachine> vmAtHost;
	private final int hostIdentifier;
	private HostCapacityEnum hostCapacityEnum;
	private HostCapacity hostCapacity;
	private boolean open;
	private boolean candidateForClosing;
	private int tickRemainingForClosing;
	private int tickHostTurnOn;
	private int currentCpuLoad;
	private int currentMemLoad;
	private int currentDemand;


	/**
	 * @Constructor
	 * @param hostCapacityEnum
	 * @param hostCapacity
	 * @param hostIdentifier
	 */
	public Host(HostCapacityEnum hostCapacityEnum,HostCapacity hostCapacity,int hostIdentifier ) {
		this.hostIdentifier = hostIdentifier;
		vmAtHost = new ArrayList<VirtualMachine>();
		this.hostCapacityEnum = hostCapacityEnum;
		this.hostCapacity = hostCapacity;
		this.open = false;
		this.candidateForClosing = false;
		this.tickRemainingForClosing = -1;
		this.tickHostTurnOn= -1;
		this.currentCpuLoad = 0;
		this.currentMemLoad = 0;
		this.currentDemand = 0;
	}

	/**
	 * @return the host id
	 */
	public int getHostId() {
		return hostIdentifier;
	}

	/**
	 * @return the type of the Host
	 */
	public HostCapacityEnum getHostCapacityEnum() {
		return hostCapacityEnum;
	}

	/**
	 * @return Host HostCapacity
	 */
	public HostCapacity getHostCapacity() {
		return hostCapacity;
	}

	/**
	 * @return true if host open, false otherwise
	 */
	public boolean isOpen() {
		return open;
	}

	/**
	 * @return true if host is candidate for closing, false otherwise
	 */
	public boolean isCandidateForClosing() {
		return candidateForClosing;
	}

	/**
	 * @return the remain tick until host need to be close
	 */
	public int getTickRemainingForClosing() {
		return tickRemainingForClosing;
	}

	/**
	 * @return the MEM load on host
	 */
	public int getCurrentMemLoad(){
		return currentMemLoad;
	}

	/**
	 * @return the CPU load on host
	 */
	public int getCurrentCpuLoad(){
		return currentCpuLoad;
	}

	/**
	 * @return the demand on host(from the VMs that deployed on him)
	 */
	public int getCurrentDemand(){
		return currentDemand;
	}

	/**
	 * @return true if host is close, false otherwise
	 */
	public boolean isClose() {
		return (!this.open);
	}

	/**
	 * open this host 
	 * 
	 * @param tick the tick that host open
	 */
	public void open(int tick) {
		this.open = true;
		this.candidateForClosing = false;
		this.tickRemainingForClosing = -1;
		this.tickHostTurnOn= tick;
		this.vmAtHost.clear();


	}

	/**
	 * @return the tick when host opened
	 */
	public int getTickHostTurnOn() {
		return tickHostTurnOn;
	}

	/**
	 * run on all VMs at Host to find the oldest VM at Host
	 * 
	 * @return the tick oldest VM start
	 */
	public int findTheOldestVmInHost(){
		int Oldest = Integer.MAX_VALUE;
		List<VirtualMachine> vmAtHost = getVmsAtHost();

		for(VirtualMachine vm : vmAtHost){
			if(vm.getStartLife() < Oldest){
				Oldest = vm.getStartLife();
			}
		}
		return Oldest;
	}

	/**
	 *  Close Host
	 */
	protected void close() {
		this.open = false;
		this.candidateForClosing = false;
		this.tickRemainingForClosing = -1;
		this.vmAtHost.clear();
		currentCpuLoad = 0;
		currentMemLoad = 0;
		currentDemand = 0;
		this.tickHostTurnOn = -1;
	}

	/**
	 * make Host candidate for closing
	 * @param tickRemainingForClosing how much tick to wait before closing
	 */
	protected void makeCandidateForClosing(int tickRemainingForClosing) {
		this.candidateForClosing = true;
		this.tickRemainingForClosing = tickRemainingForClosing;
	}

	/**
	 * remove Host from candidate for closing status
	 */
	protected void remCandidateForClosing() {
		if(this.candidateForClosing) {
			this.candidateForClosing = false;
			this.tickRemainingForClosing = -1;
		}
	}

	/**
	 * Set the host CPU load
	 * 
	 * @param cpuLoad the CPU  load on host
	 */
	protected void setCurrentCpuLoad(int cpuLoad){
		currentCpuLoad = cpuLoad;
	}

	/**
	 * Set the host MEM load.
	 * 
	 * @param memLoad the MEM  load on host
	 */
	protected void setCurrentMemLoad(int memLoad){
		currentMemLoad = memLoad;
	}

	/**
	 * Update the host CPU load
	 * 
	 * @param cpuLoad the CPU  load on host
	 */
	protected void updateCurrentCpuLoad(int cpuLoad){
		currentCpuLoad = currentCpuLoad+cpuLoad;
	}

	/**
	 * Update the host MEM load
	 * 
	 * @param memLoad the MEM  load on host
	 */
	protected void updateCurrentMemLoad(int memLoad){
		currentMemLoad = currentMemLoad+memLoad;
	}

	/**
	 * Set the CPU demand on host.
	 * 
	 * @param cpuDemand the CPU demand
	 */
	protected void setCurrentCpuDemand(int cpuDemand){
		currentDemand = cpuDemand;
	}

	/**
	 * Add VM to host
	 * 
	 * @param vm the VM to be add
	 * @param cpuLoad the CPU load of VM
	 * @param memLoad the MEM load of VM
	 */
	protected void addVmToHost(VirtualMachine vm,int cpuLoad,int memLoad ) {
		if(!vmAtHost.contains(vm)) {
			vmAtHost.add(vm);
			updateCurrentCpuLoad(cpuLoad);
			updateCurrentMemLoad(memLoad);
		}
	}

	/**
	 * @return the VMs that deployed on Host
	 */
	protected List<VirtualMachine> getVmsAtHost () {
		return vmAtHost;
	}


	/**
	 * Remove VMs from Host
	 * 
	 * @param removeList the list of VMs
	 */
	protected void removeListOfVmFromHost(List<VirtualMachine> removeList) {
		for(VirtualMachine vm : removeList) {
			removeVmFromHost(vm);
		}
	}

	/**
	 * Remove VM from Host
	 * 
	 * @param vm the VM to remove
	 */
	protected void removeVmFromHost(VirtualMachine vm) {
		if(vmAtHost.contains(vm)) {
			int cpuTorem = vm.getSatisfiedCpuDemand();
			int memTorem = vm.getSatisfiedMemDemand();
			updateCurrentCpuLoad(-cpuTorem);
			updateCurrentMemLoad(-memTorem);
			vmAtHost.remove(vm);
		}
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "[Host, ID: " + hostIdentifier + ", CAPACITY: "
				+ hostCapacityEnum + "]";
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + hostIdentifier;
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Host other = (Host) obj;
		if (hostIdentifier != other.hostIdentifier)
			return false;
		return true;
	}

}
